﻿#include "Map.h"
#include "../Settings.h"
#include "../../Shared/Globals.h"
#include "../MirDatabase/NPCInfo.h"
#include "../../Shared/ServerPackets.h"
#include "../Logger.h"
#include "../MirObjects/MonsterObject.h"
#include "../MirDatabase/MagicInfo.h"
#include "../../Shared/Functions/Functions.h"
#include "../../Shared/Data/Stat.h"

using namespace Server::MirDatabase;
using namespace Server::MirObjects;
namespace S = ServerPackets;

namespace Server::MirEnvir
{

    Server::MirEnvir::Envir *Map::getEnvir()
    {
        return Envir::getMain();
    }

    Server::MessageQueue *Map::getMessageQueue()
    {
        return MessageQueue::getInstance();
    }

    Map::Map(MapInfo *info)
    {
        Info = info;
        Thread = getEnvir()->Random->Next(Settings::ThreadLimit);
    }

    Door *Map::AddDoor(unsigned char DoorIndex, Point *location)
    {
        DoorIndex = static_cast<unsigned char>(DoorIndex & 0x7F);
        for (int i = 0; i < Doors.size(); i++)
        {
            if (Doors[i]->index == DoorIndex)
            {
                return Doors[i];
            }
        }
        Door *DoorInfo = new Door();
        DoorInfo->index = DoorIndex;
        DoorInfo->Location = location;
        Doors.push_back(DoorInfo);

//C# TO C++ CONVERTER TODO TASK: A 'delete DoorInfo' statement was not added since DoorInfo was passed to a method or constructor. Handle memory management manually.
        return DoorInfo;
    }

    bool Map::OpenDoor(unsigned char DoorIndex)
    {
        for (int i = 0; i < Doors.size(); i++)
        {
            if (Doors[i]->index == DoorIndex)
            {
                Doors[i]->DoorState = DoorState::Open;
                Doors[i]->LastTick = getEnvir()->getTime();
                return true;
            }
        }
        return false;
    }

    unsigned char Map::FindType(std::vector<unsigned char> &input)
    {
        //c# custom map format
        if ((input[2] == 0x43) && (input[3] == 0x23))
        {
            return 100;
        }
        //wemade mir3 maps have no title they just start with blank bytes
        if (input[0] == 0)
        {
            return 5;
        }
        //shanda mir3 maps start with title: (C) SNDA, MIR3.
        if ((input[0] == 0x0F) && (input[5] == 0x53) && (input[14] == 0x33))
        {
            return 6;
        }

        //wemades antihack map (laby maps) title start with: Mir2 AntiHack
        if ((input[0] == 0x15) && (input[4] == 0x32) && (input[6] == 0x41) && (input[19] == 0x31))
        {
            return 4;
        }

        //wemades 2010 map format i guess title starts with: Map 2010 Ver 1.0
        if ((input[0] == 0x10) && (input[2] == 0x61) && (input[7] == 0x31) && (input[14] == 0x31))
        {
            return 1;
        }

        //shanda's 2012 format and one of shandas(wemades) older formats share same header info, only difference is the filesize
        if ((input[4] == 0x0F) && (input[18] == 0x0D) && (input[19] == 0x0A))
        {
            int W = input[0] + (input[1] << 8);
            int H = input[2] + (input[3] << 8);
            if (input.size() > (52 + (W * H * 14)))
            {
                return 3;
            }
            else
            {
                return 2;
            }
        }

        //3/4 heroes map format (myth/lifcos i guess)
        if ((input[0] == 0x0D) && (input[1] == 0x4C) && (input[7] == 0x20) && (input[11] == 0x6D))
        {
            return 7;
        }
        return 0;
    }

    void Map::LoadMapCellsv0(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 0;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 52;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 12
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }

                offSet += 2;

                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //No Floor Tile.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }

                offSet += 4;

                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }

                offSet += 3;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }
            }
        }
    }

    void Map::LoadMapCellsv1(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 21;

        int w = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        int xor_Keyword = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        int h = BitConverter::ToInt16(fileBytes, offSet);
        Width = w ^ xor_Keyword;
        Height = h ^ xor_Keyword;
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 54;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                if (((BitConverter::ToInt32(fileBytes, offSet) ^ 0xAA38AA38) & 0x20000000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }

                offSet += 6;
                if (((BitConverter::ToInt16(fileBytes, offSet) ^ xor_Keyword) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //No Floor Tile.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 2;
                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }
                offSet += 5;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }

                offSet += 1;
            }
        }
    }

    void Map::LoadMapCellsv2(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 0;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 52;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 14
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //No Floor Tile.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }

                offSet += 2;
                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }
                offSet += 5;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }

                offSet += 2;
            }
        }
    }

    void Map::LoadMapCellsv3(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 0;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 52;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 36
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //No Floor Tile.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 2;
                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }
                offSet += 12;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }

                offSet += 17;
            }
        }
    }

    void Map::LoadMapCellsv4(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 31;
        int w = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        int xor_Keyword = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        int h = BitConverter::ToInt16(fileBytes, offSet);
        Width = w ^ xor_Keyword;
        Height = h ^ xor_Keyword;
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 64;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 12
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }

                offSet += 2;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 4;
                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }
                offSet += 6;
            }
        }
    }

    void Map::LoadMapCellsv5(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 22;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 28 + (3 * ((Width / 2) + (Width % 2)) * (Height / 2));
        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 14
                if ((fileBytes[offSet] & 0x01) != 1)
                {
                    Cells[x][y] = Cell::HighWall;
                }
                else if ((fileBytes[offSet] & 0x02) != 2)
                {
                    Cells[x][y] = Cell::LowWall;
                }
                else
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 13;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }
            }
        }
    }

    void Map::LoadMapCellsv6(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 16;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 2;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 40;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 20
                if ((fileBytes[offSet] & 0x01) != 1)
                {
                    Cells[x][y] = Cell::HighWall;
                }
                else if ((fileBytes[offSet] & 0x02) != 2)
                {
                    Cells[x][y] = Cell::LowWall;
                }
                else
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 20;
            }
        }
    }

    void Map::LoadMapCellsv7(std::vector<unsigned char> &fileBytes)
    {
        int offSet = 21;
        Width = BitConverter::ToInt16(fileBytes, offSet);
        offSet += 4;
        Height = BitConverter::ToInt16(fileBytes, offSet);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offSet = 54;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            { //total 15
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }
                offSet += 6;
                if ((BitConverter::ToInt16(fileBytes, offSet) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }
                //offSet += 2;
                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offSet += 2;
                if (fileBytes[offSet] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(fileBytes[offSet], &tempVar2);
                }
                offSet += 4;

                unsigned char light = fileBytes[offSet++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }

                offSet += 2;
            }
        }
    }

    void Map::LoadMapCellsV100(std::vector<unsigned char> &Bytes)
    {
        int offset = 4;
        if ((Bytes[0] != 1) || (Bytes[1] != 0))
        {
            return; //only support version 1 atm
        }
        Width = BitConverter::ToInt16(Bytes, offset);
        offset += 2;
        Height = BitConverter::ToInt16(Bytes, offset);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Cells = new Cell[Width, Height];
        Cells = RectangularVectors::RectangularCellVector(Width, Height);
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: DoorIndex = new Door[Width, Height];
        DoorIndex = RectangularVectors::RectangularDoorVector(Width, Height);

        offset = 8;

        for (int x = 0; x < Width; x++)
        {
            for (int y = 0; y < Height; y++)
            {
                offset += 2;
                if ((BitConverter::ToInt32(Bytes, offset) & 0x20000000) != 0)
                {
                    Cells[x][y] = Cell::HighWall; //Can Fire Over.
                }
                offset += 10;
                if ((BitConverter::ToInt16(Bytes, offset) & 0x8000) != 0)
                {
                    Cells[x][y] = Cell::LowWall; //Can't Fire Over.
                }

                if (Cells[x][y].empty())
                {
                    Cell *tempVar = new Cell();
                    tempVar->Attribute = CellAttribute::Walk;
                    Cells[x][y] = tempVar;

                    delete tempVar;
                }
                offset += 2;
                if (Bytes[offset] > 0)
                {
                    Point tempVar2(x, y);
                    DoorIndex[x][y] = AddDoor(Bytes[offset], &tempVar2);
                }
                offset += 11;

                unsigned char light = Bytes[offset++];

                if (light >= 100 && light <= 119)
                {
                    Cells[x][y].FishingAttribute = static_cast<signed char>(light - 100);
                }
            }
        }

    }

    bool Map::Load()
    {
        try
        {
            std::string fileName = FileSystem::combine(Settings::MapPath, Info->FileName + ".map");
            if (FileSystem::fileExists(fileName))
            {
                std::vector<unsigned char> fileBytes = File::ReadAllBytes(fileName);
                switch (FindType(fileBytes))
                {
                    case 0:
                        LoadMapCellsv0(fileBytes);
                        break;
                    case 1:
                        LoadMapCellsv1(fileBytes);
                        break;
                    case 2:
                        LoadMapCellsv2(fileBytes);
                        break;
                    case 3:
                        LoadMapCellsv3(fileBytes);
                        break;
                    case 4:
                        LoadMapCellsv4(fileBytes);
                        break;
                    case 5:
                        LoadMapCellsv5(fileBytes);
                        break;
                    case 6:
                        LoadMapCellsv6(fileBytes);
                        break;
                    case 7:
                        LoadMapCellsv7(fileBytes);
                        break;
                    case 100:
                        LoadMapCellsV100(fileBytes);
                        break;
                }

                for (int i = 0; i < Info->Respawns.size(); i++)
                {
                    MapRespawn *info = new MapRespawn(Info->Respawns[i]);
                    if (info->Monster == nullptr)
                    {
                        delete info;
                        continue;
                    }
                    info->Map = this;
                    Respawns.push_back(info);

                    if ((info->Info->SaveRespawnTime) && (info->Info->RespawnTicks != 0))
                    {
                        getEnvir()->SavedSpawns.push_back(info);
                    }

//C# TO C++ CONVERTER TODO TASK: A 'delete info' statement was not added since info was passed to a method or constructor. Handle memory management manually.
                }

                for (int i = 0; i < Info->NPCs.size(); i++)
                {
                    NPCInfo *info = Info->NPCs[i];
                    if (!ValidPoint(info->Location))
                    {
                        continue;
                    }

                    NPCObject *tempVar = new NPCObject(info);
                    tempVar->setCurrentMap(this);
                    AddObject(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
                }

                for (int i = 0; i < Info->SafeZones.size(); i++)
                {
                    CreateSafeZone(Info->SafeZones[i]);
                }

                CreateMine();

                return true;
            }
        }
        catch (const std::runtime_error &ex)
        {
            getMessageQueue()->Enqueue(ex);
        }

        getMessageQueue()->Enqueue("Failed to Load Map: " + Info->FileName);
        return false;
    }

    void Map::CreateSafeZone(SafeZoneInfo *info)
    {
        if (Settings::SafeZoneBorder)
        {
            for (int y = info->Location->Y - info->Size; y <= info->Location->Y + info->Size; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= Height)
                {
                    break;
                }
                for (int x = info->Location->X - info->Size; x <= info->Location->X + info->Size; x += std::abs(y - info->Location->Y) == info->Size ? 1 : info->Size * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= Width)
                    {
                        break;
                    }
                    if (!Cells[x][y].Valid)
                    {
                        continue;
                    }

                    SpellObject *spell = new SpellObject();
                    spell->ExpireTime = std::numeric_limits<long long>::max();
                    spell->Spell = Spell::TrapHexagon;
                    spell->TickSpeed = std::numeric_limits<int>::max();
                    Point tempVar(x, y);
                    spell->setCurrentLocation(&tempVar);
                    spell->setCurrentMap(this);
                    spell->Decoration = true;

                    Cells[x][y].push_back(spell);

                    spell->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete spell' statement was not added since spell was passed to a method or constructor. Handle memory management manually.
                }
            }
        }

        if (Settings::SafeZoneHealing)
        {
            for (int y = info->Location->Y - info->Size; y <= info->Location->Y + info->Size; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= Height)
                {
                    break;
                }
                for (int x = info->Location->X - info->Size; x <= info->Location->X + info->Size; x++)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= Width)
                    {
                        break;
                    }
                    if (!Cells[x][y].Valid)
                    {
                        continue;
                    }

                    SpellObject *spell = new SpellObject();
                    spell->ExpireTime = std::numeric_limits<long long>::max();
                    spell->Value = 25;
                    spell->TickSpeed = 2000;
                    spell->Spell = Spell::Healing;
                    Point tempVar2(x, y);
                    spell->setCurrentLocation(&tempVar2);
                    spell->setCurrentMap(this);

                    Cells[x][y].push_back(spell);

                    spell->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete spell' statement was not added since spell was passed to a method or constructor. Handle memory management manually.
                }
            }
        }


    }

    void Map::CreateMine()
    {
        if ((Info->MineIndex == 0) && (Info->MineZones.empty()))
        {
            return;
        }
//C# TO C++ CONVERTER NOTE: The following call to the 'RectangularVectors' helper class reproduces the rectangular array initialization that is automatic in C#:
//ORIGINAL LINE: Mine = new MineSpot[Width, Height];
        Mine = RectangularVectors::RectangularMineSpotVector(Width, Height);
        for (int i = 0; i < Width; i++)
        {
            for (int j = 0; j < Height; j++)
            {
                Mine[i][j] = new MineSpot();
            }
        }
        if ((Info->MineIndex != 0) && (Settings::MineSetList.size() > Info->MineIndex - 1))
        {
            Settings::MineSetList[Info->MineIndex - 1]->SetDrops(getEnvir()->ItemInfoList);
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    Mine[i][j].Mine = Settings::MineSetList[Info->MineIndex - 1];
                }
            }
        }
        if (Info->MineZones.size() > 0)
        {
            for (int i = 0; i < Info->MineZones.size(); i++)
            {
                MineZone *Zone = Info->MineZones[i];
                if (Zone->Mine != 0)
                {
                    Settings::MineSetList[Zone->Mine - 1]->SetDrops(getEnvir()->ItemInfoList);
                }
                if (Settings::MineSetList.size() < Zone->Mine)
                {
                    continue;
                }
                for (int x = Zone->Location->X - Zone->Size; x < Zone->Location->X + Zone->Size; x++)
                {
                    for (int y = Zone->Location->Y - Zone->Size; y < Zone->Location->Y + Zone->Size; y++)
                    {
                        if ((x < 0) || (x >= Width) || (y < 0) || (y >= Height))
                        {
                            continue;
                        }
                        if (Zone->Mine == 0)
                        {
                            Mine[x][y].Mine = nullptr;
                        }
                        else
                        {
                            Mine[x][y].Mine = Settings::MineSetList[Zone->Mine - 1];
                        }
                    }
                }
            }
        }
    }

    Cell *Map::GetCell(Point *location)
    {
        return Cells[location->X][location->Y];
    }

    Cell *Map::GetCell(int x, int y)
    {
        return Cells[x][y];
    }

    bool Map::ValidPoint(Point *location)
    {
        return location->X >= 0 && location->X < Width && location->Y >= 0 && location->Y < Height && GetCell(location)->getValid();
    }

    bool Map::ValidPoint(int x, int y)
    {
        return x >= 0 && x < Width && y >= 0 && y < Height && GetCell(x, y)->getValid();
    }

    bool Map::CheckDoorOpen(Point *location)
    {
        if (DoorIndex[location->X][location->Y].empty())
        {
            return true;
        }
        if (DoorIndex[location->X][location->Y].DoorState != DoorState::Open)
        {
            return false;
        }
        return true;
    }

    void Map::Process()
    {
        ProcessRespawns();

        for (int i = 0; i < Doors.size(); i++)
        {
            if ((Doors[i]->DoorState == DoorState::Open) && (Doors[i]->LastTick + 5000 < getEnvir()->getTime()))
            {
                Doors[i]->DoorState = (DoorState)0;

                S::Opendoor *tempVar = new S::Opendoor(), *Doors;
                tempVar->DoorIndex = Doors[i]->index;
                tempVar->Close = true;
                Broadcast(tempVar, Doors[i]->Location);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
            }
        }

        if ((Info->Lightning) && getEnvir()->getTime() > LightningTime)
        {
            LightningTime = getEnvir()->getTime() + getEnvir()->Random->Next(3000, 15000);
            for (int i = Players.size() - 1; i >= 0; i--)
            {
                PlayerObject *player = Players[i];
                Point *location;
                if (getEnvir()->Random->Next(4) == 0)
                {
                    location = player->getCurrentLocation();
                }
                else
                {
                    location = new Point(player->getCurrentLocation()->X - 10 + getEnvir()->Random->Next(20), player->getCurrentLocation()->Y - 10 + getEnvir()->Random->Next(20));
                }

                if (!ValidPoint(location))
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
                    continue;
                }

                SpellObject *lightning = new SpellObject();
                lightning->Spell = Spell::MapLightning;
                lightning->Value = getEnvir()->Random->Next(Info->LightningDamage);
                lightning->ExpireTime = getEnvir()->getTime() + (1000);
                lightning->TickSpeed = 500;
                lightning->Caster = nullptr;
                lightning->setCurrentLocation(location);
                lightning->setCurrentMap(this);
                lightning->setDirection(MirDirection::Up);
                AddObject(lightning);
                lightning->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete lightning' statement was not added since lightning was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
            }
        }

        if ((Info->Fire) && getEnvir()->getTime() > FireTime)
        {
            FireTime = getEnvir()->getTime() + getEnvir()->Random->Next(3000, 15000);
            for (int i = Players.size() - 1; i >= 0; i--)
            {
                PlayerObject *player = Players[i];
                Point *location;
                if (getEnvir()->Random->Next(4) == 0)
                {
                    location = player->getCurrentLocation();

                }
                else
                {
                    location = new Point(player->getCurrentLocation()->X - 10 + getEnvir()->Random->Next(20), player->getCurrentLocation()->Y - 10 + getEnvir()->Random->Next(20));
                }

                if (!ValidPoint(location))
                {
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
                    continue;
                }

                SpellObject *lightning = new SpellObject();
                lightning->Spell = Spell::MapLava;
                lightning->Value = getEnvir()->Random->Next(Info->FireDamage);
                lightning->ExpireTime = getEnvir()->getTime() + (1000);
                lightning->TickSpeed = 500;
                lightning->Caster = nullptr;
                lightning->setCurrentLocation(location);
                lightning->setCurrentMap(this);
                lightning->setDirection(MirDirection::Up);
                AddObject(lightning);
                lightning->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete lightning' statement was not added since lightning was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
            }
        }

        for (int i = 0; i < ActionList.size(); i++)
        {
            if (getEnvir()->getTime() < ActionList[i]->Time)
            {
                continue;
            }
            Process(ActionList[i]);
            ActionList.erase(ActionList.begin() + i);
        }
    }

    void Map::ProcessRespawns()
    {
        bool Success = true;
        for (int i = 0; i < Respawns.size(); i++)
        {
            MapRespawn *respawn = Respawns[i];
            if ((respawn->Info->RespawnTicks != 0) && (getEnvir()->RespawnTick->CurrentTickcounter < respawn->NextSpawnTick))
            {
                continue;
            }
            if ((respawn->Info->RespawnTicks == 0) && (getEnvir()->getTime() < respawn->RespawnTime))
            {
                continue;
            }

            if (respawn->Count < (respawn->Info->Count * getEnvir()->SpawnMultiplier))
            {
                int count = (respawn->Info->Count * getEnvir()->SpawnMultiplier) - respawn->Count;

                for (int c = 0; c < count; c++)
                {
                    Success = respawn->Spawn();
                }
            }
            if (Success)
            {
                respawn->ErrorCount = 0;
                long long delay = std::max(1, respawn->Info->Delay - respawn->Info->RandomDelay + getEnvir()->Random->Next(respawn->Info->RandomDelay * 2));
                respawn->RespawnTime = getEnvir()->getTime() + (delay * Settings::Minute);
                if (respawn->Info->RespawnTicks != 0)
                {
                    respawn->NextSpawnTick = getEnvir()->RespawnTick->CurrentTickcounter + static_cast<unsigned long long>(respawn->Info->RespawnTicks);
                    if (respawn->NextSpawnTick > std::numeric_limits<long long>::max()) //since nextspawntick is ulong this simple thing allows an easy way of preventing the counter from overflowing
                    {
                        respawn->NextSpawnTick -= std::numeric_limits<long long>::max();
                    }
                }
            }
            else
            {
                respawn->RespawnTime = getEnvir()->getTime() + 1 * Settings::Minute; // each time it fails to spawn, give it a 1 minute cooldown
                if (respawn->ErrorCount < 5)
                {
                    respawn->ErrorCount++;
                }
                else
                {
                    if (respawn->ErrorCount == 5)
                    {
                        respawn->ErrorCount++;

                        Logger::GetLogger(LogType::Spawn)->Info(std::string::Format("Failed to spawn: ") + StringHelper::formatSimple("mapindex: {0}, ", respawn->Map->Info->Index) + StringHelper::formatSimple("mob info: index: {0}, ", respawn->Info->MonsterIndex) + StringHelper::formatSimple("spawncoords ({0}:{1}), ", respawn->Info->Location->X, respawn->Info->Location->Y) + StringHelper::formatSimple("range {0}", respawn->Info->Spread));
                    }

                }
            }
        }
    }

    void Map::Process(DelayedAction *action)
    {
        switch (action->Type)
        {
            case DelayedType::Magic:
                CompleteMagic(action->Params);
                break;
            case DelayedType::Spawn:
            {
                MapObject *obj = std::any_cast<MapObject*>(action->Params[0]);

                switch (obj->getRace())
                {
                    case ObjectType::Monster:
                    {
                            MonsterObject *mob = std::any_cast<MonsterObject*>(action->Params[0]);
                            mob->Spawn(this, std::any_cast<Point*>(action->Params[1]));
                            if (action->Params.size() > 2)
                            {
                                (std::any_cast<MonsterObject*>(action->Params[2])).SlaveList->Add(mob);
                            }
                    }
                        break;
                    case ObjectType::Spell:
                    {
                            SpellObject *spell = std::any_cast<SpellObject*>(action->Params[0]);
                            AddObject(spell);
                            spell->Spawned();
                    }
                        break;
                }
                break;
            }
        }
    }

    void Map::CompleteMagic(std::vector<std::any> &data)
    {
        bool train = false;
        PlayerObject *player = std::any_cast<PlayerObject*>(data[0]);
        UserMagic *magic = std::any_cast<UserMagic*>(data[1]);

        if (player == nullptr || player->Info == nullptr)
        {
            return;
        }

        int value, value2;
        Point *location;
        Cell *cell;
        MirDirection dir;
        MonsterObject *monster;
        Point *front;
        switch (magic->Spell)
        {

            #pragma region HellFire

            case Spell::HellFire:
            {
                value = std::any_cast<int>(data[2]);
                dir = std::any_cast<MirDirection>(data[4]);
                location = Functions::PointMove(std::any_cast<Point*>(data[3]), dir, 1);
                int count = std::any_cast<int>(data[5]) - 1;

                if (!ValidPoint(location))
                {
                    return;
                }

                if (count > 0)
                {
                    DelayedAction *action = new DelayedAction(DelayedType::Magic, getEnvir()->getTime() + 100, {player, magic, value, location, dir, count});
                    ActionList.push_back(action);

//C# TO C++ CONVERTER TODO TASK: A 'delete action' statement was not added since action was passed to a method or constructor. Handle memory management manually.
                }

                cell = GetCell(location);

                if (cell->Objects.empty())
                {
                    return;
                }


                for (int i = 0; i < cell->Objects.size(); i++)
                {
                    MapObject *target = cell->Objects[i];
                    switch (target->getRace())
                    {
                        case ObjectType::Monster:
                        case ObjectType::Player:
                            //Only targets
                            if (target->IsAttackTarget(player))
                            {
                                if (target->Attacked(player, value, DefenceType::MAC, false) > 0)
                                {
                                    train = true;
                                }
                                return;
                            }
                            break;
                    }
                }
                break;

            #pragma endregion

            #pragma region SummonSkeleton, SummonShinsu, SummonHolyDeva, ArcherSummons

            }
            case Spell::SummonSkeleton:
            case Spell::SummonShinsu:
            case Spell::SummonHolyDeva:
            case Spell::SummonVampire:
            case Spell::SummonToad:
            case Spell::SummonSnakes:
                monster = std::any_cast<MonsterObject*>(data[2]);
                front = std::any_cast<Point*>(data[3]);

                if (monster->Master->Dead)
                {
                    return;
                }

                if (ValidPoint(front))
                {
                    monster->Spawn(this, front);
                }
                else
                {
                    monster->Spawn(player->getCurrentMap(), player->getCurrentLocation());
                }

                monster->Master->Pets.push_back(monster);
                break;

            #pragma endregion

            #pragma region FireBang, IceStorm

            case Spell::IceStorm:
            case Spell::FireBang:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsAttackTarget(player))
                                    {
                                        if (target->Attacked(player, value, DefenceType::MAC, false) > 0)
                                        {
                                            train = true;
                                        }
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region MassHiding

            case Spell::MassHiding:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsFriendlyTarget(player))
                                    {
                                        if (target->HasBuff(BuffType::Hiding, _))
                                        {
                                            return;
                                        }

                                        Stats tempVar();
                                        target->AddBuff(BuffType::Hiding, player, (Settings::Second * value), &tempVar);
                                        target->OperateTime = 0;
                                        train = true;
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region SoulShield, BlessedArmour

            case Spell::SoulShield:
            case Spell::BlessedArmour:
            {
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                BuffType type = magic->Spell == Spell::SoulShield ? BuffType::SoulShield : BuffType::BlessedArmour;

                for (int y = location->Y - 3; y <= location->Y + 3; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 3; x <= location->X + 3; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsFriendlyTarget(player))
                                    {
                                        auto stats = new Stats();
                                        stats.[type == BuffType::SoulShield ? Stat::MaxAC : Stat::MaxMAC] = target->getLevel() / 7 + 4;

                                        target->AddBuff(type, player, Settings::Second * value, stats);
                                        target->OperateTime = 0;
                                        train = true;

//C# TO C++ CONVERTER TODO TASK: A 'delete stats' statement was not added since stats was passed to a method or constructor. Handle memory management manually.
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region FireWall

            }
            case Spell::FireWall:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                player->LevelMagic(magic);

                if (ValidPoint(location))
                {
                    cell = GetCell(location);

                    bool cast = true;
                    if (!cell->Objects.empty())
                    {
                        for (int o = 0; o < cell->Objects.size(); o++)
                        {
                            MapObject *target = cell->Objects[o];
                            if (target->getRace() != ObjectType::Spell || (static_cast<SpellObject*>(target))->Spell != Spell::FireWall)
                            {
                                continue;
                            }

                            cast = false;
                            break;
                        }
                    }

                    if (cast)
                    {
                        SpellObject *ob = new SpellObject();
                        ob->Spell = Spell::FireWall;
                        ob->Value = value;
                        ob->ExpireTime = getEnvir()->getTime() + (10 + value / 2) * 1000;
                        ob->TickSpeed = 2000;
                        ob->Caster = player;
                        ob->setCurrentLocation(location);
                        ob->setCurrentMap(this);
                        AddObject(ob);
                        ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                    }
                }

                dir = MirDirection::Up;
                for (int i = 0; i < 4; i++)
                {
                    location = Functions::PointMove(std::any_cast<Point*>(data[3]), dir, 1);
                    dir += 2;

                    if (!ValidPoint(location))
                    {
                        continue;
                    }

                    cell = GetCell(location);
                    bool cast = true;

                    if (!cell->Objects.empty())
                    {
                        for (int o = 0; o < cell->Objects.size(); o++)
                        {
                            MapObject *target = cell->Objects[o];
                            if (target->getRace() != ObjectType::Spell || (static_cast<SpellObject*>(target))->Spell != Spell::FireWall)
                            {
                                continue;
                            }

                            cast = false;
                            break;
                        }
                    }

                    if (!cast)
                    {
                        continue;
                    }

                    SpellObject *ob = new SpellObject();
                    ob->Spell = Spell::FireWall;
                    ob->Value = value;
                    ob->ExpireTime = getEnvir()->getTime() + (10 + value / 2) * 1000;
                    ob->TickSpeed = 2000;
                    ob->Caster = player;
                    ob->setCurrentLocation(location);
                    ob->setCurrentMap(this);
                    AddObject(ob);
                    ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                }

                break;

            #pragma endregion

            #pragma region Lightning

            case Spell::Lightning:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                dir = std::any_cast<MirDirection>(data[4]);

                for (int i = 0; i < 6; i++)
                {
                    location = Functions::PointMove(location, dir, 1);

                    if (!ValidPoint(location))
                    {
                        continue;
                    }

                    cell = GetCell(location);

                    if (cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int o = 0; o < cell->Objects.size(); o++)
                    {
                        MapObject *target = cell->Objects[o];
                        if (target->getRace() != ObjectType::Player && target->getRace() != ObjectType::Monster)
                        {
                            continue;
                        }

                        if (!target->IsAttackTarget(player))
                        {
                            continue;
                        }
                        if (target->Attacked(player, value, DefenceType::MAC, false) > 0)
                        {
                            train = true;
                        }
                        break;
                    }
                }

                break;

            #pragma endregion

            #pragma region HeavenlySword

            case Spell::HeavenlySword:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                dir = std::any_cast<MirDirection>(data[4]);

                for (int i = 0; i < 3; i++)
                {
                    location = Functions::PointMove(location, dir, 1);

                    if (!ValidPoint(location))
                    {
                        continue;
                    }

                    cell = GetCell(location);

                    if (cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int o = 0; o < cell->Objects.size(); o++)
                    {
                        MapObject *target = cell->Objects[o];
                        if (target->getRace() != ObjectType::Player && target->getRace() != ObjectType::Monster)
                        {
                            continue;
                        }

                        if (!target->IsAttackTarget(player))
                        {
                            continue;
                        }
                        if (target->Attacked(player, value, DefenceType::MAC, false) > 0)
                        {
                            train = true;
                        }
                        break;
                    }
                }

                break;

            #pragma endregion

            #pragma region MassHealing

            case Spell::MassHealing:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsFriendlyTarget(player))
                                    {
                                        if (target->getHealth() >= target->getMaxHealth())
                                        {
                                            continue;
                                        }
                                        target->HealAmount = static_cast<unsigned short>(std::min(std::numeric_limits<unsigned short>::max(), target->HealAmount + value));
                                        target->OperateTime = 0;
                                        train = true;
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region ThunderStorm

            case Spell::ThunderStorm:
            case Spell::FlameField:
            case Spell::NapalmShot:
            case Spell::StormEscape:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (!target->IsAttackTarget(player))
                                    {
                                        break;
                                    }

                                    if (target->Attacked(player, magic->Spell == Spell::ThunderStorm && !target->Undead ? value / 10 : value, DefenceType::MAC, false) <= 0)
                                    {
                                        if (target->Undead)
                                        {
                                            Poison *tempVar2 = new Poison(), *player;
                                            tempVar2->PType = PoisonType::Stun;
                                            tempVar2->Duration = magic->Level + 2;
                                            tempVar2->TickSpeed = 1000;
                                            target->ApplyPoison(tempVar2, player);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
                                        }
                                        break;
                                    }

                                    train = true;
                                    break;
                            }
                        }

                    }
                }

                break;

            #pragma endregion

            #pragma region LionRoar

            case Spell::LionRoar:
                location = std::any_cast<Point*>(data[2]);

                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            if (target->getRace() != ObjectType::Monster)
                            {
                                continue;
                            }
                            //Only targets
                            if (!target->IsAttackTarget(player) || player->getLevel() + 3 < target->getLevel())
                            {
                                continue;
                            }
                            Poison *tempVar3 = new Poison(), *player;
                            tempVar3->PType = PoisonType::LRParalysis;
                            tempVar3->Duration = magic->Level + 2;
                            tempVar3->TickSpeed = 1000;
                            target->ApplyPoison(tempVar3, player);
                            target->OperateTime = 0;
                            train = true;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region PoisonCloud

            case Spell::PoisonCloud:
            {
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                unsigned char bonusdmg = std::any_cast<unsigned char>(data[4]);
                train = true;
                bool show = true;

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid())
                        {
                            continue;
                        }

                        bool cast = true;
                        if (!cell->Objects.empty())
                        {
                            for (int o = 0; o < cell->Objects.size(); o++)
                            {
                                MapObject *target = cell->Objects[o];
                                if (target->getRace() != ObjectType::Spell || (static_cast<SpellObject*>(target))->Spell != Spell::PoisonCloud)
                                {
                                    continue;
                                }

                                cast = false;
                                break;
                            }
                        }

                        if (!cast)
                        {
                            continue;
                        }

                        SpellObject *ob = new SpellObject();
                        ob->Spell = Spell::PoisonCloud;
                        ob->Value = value + bonusdmg;
                        ob->ExpireTime = getEnvir()->getTime() + 6000;
                        ob->TickSpeed = 1000;
                        ob->Caster = player;
                        Point tempVar4(x, y);
                        ob->setCurrentLocation(&tempVar4);
                        ob->CastLocation = location;
                        ob->Show = show;
                        ob->setCurrentMap(this);

                        show = false;

                        AddObject(ob);
                        ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                    }
                }

                break;

            #pragma endregion

            #pragma region IceThrust

            }
            case Spell::IceThrust:
            {
                    location = std::any_cast<Point*>(data[2]);
                    MirDirection direction = std::any_cast<MirDirection>(data[3]);

                    int nearDamage = std::any_cast<int>(data[4]);
                    int farDamage = std::any_cast<int>(data[5]);

                    int col = 3;
                    int row = 3;

                    std::vector<Point*> loc(col); //0 = left 1 = center 2 = right
                    loc[0] = Functions::PointMove(location, Functions::PreviousDir(direction), 1);
                    loc[1] = Functions::PointMove(location, direction, 1);
                    loc[2] = Functions::PointMove(location, Functions::NextDir(direction), 1);

                    for (int i = 0; i < col; i++)
                    {
                        Point *startPoint = loc[i];
                        for (int j = 0; j < row; j++)
                        {
                            Point *hitPoint = Functions::PointMove(startPoint, direction, j);

                            if (!ValidPoint(hitPoint))
                            {
                                continue;
                            }

                            cell = GetCell(hitPoint);

                            if (cell->Objects.empty())
                            {
                                continue;
                            }

                            for (int k = 0; k < cell->Objects.size(); k++)
                            {
                                MapObject *target = cell->Objects[k];
                                switch (target->getRace())
                                {
                                    case ObjectType::Monster:
                                    case ObjectType::Player:
                                        if (target->IsAttackTarget(player))
                                        {
                                            //Only targets
                                            if (target->Attacked(player, j <= 1 ? nearDamage : farDamage, DefenceType::MAC, false) > 0)
                                            {
                                                if (player->getLevel() + (target->getRace() == ObjectType::Player ? 2 : 10) >= target->getLevel() && getEnvir()->Random->Next(target->getRace() == ObjectType::Player ? 100 : 20) <= magic->Level)
                                                {
                                                    Poison *tempVar5 = new Poison(), *player;
                                                    tempVar5->Owner = player;
                                                    tempVar5->Duration = target->getRace() == ObjectType::Player ? 4 : 5 + getEnvir()->Random->Next(5);
                                                    tempVar5->PType = PoisonType::Slow;
                                                    tempVar5->TickSpeed = 1000;
                                                    target->ApplyPoison(tempVar5, player);
                                                    target->OperateTime = 0;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar5' statement was not added since tempVar5 was passed to a method or constructor. Handle memory management manually.
                                                }

                                                if (player->getLevel() + (target->getRace() == ObjectType::Player ? 2 : 10) >= target->getLevel() && getEnvir()->Random->Next(target->getRace() == ObjectType::Player ? 100 : 40) <= magic->Level)
                                                {
                                                    Poison *tempVar6 = new Poison(), *player;
                                                    tempVar6->Owner = player;
                                                    tempVar6->Duration = target->getRace() == ObjectType::Player ? 2 : 5 + getEnvir()->Random->Next(player->Stats[Stat::Freezing]);
                                                    tempVar6->PType = PoisonType::Frozen;
                                                    tempVar6->TickSpeed = 1000;
                                                    target->ApplyPoison(tempVar6, player);
                                                    target->OperateTime = 0;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar6' statement was not added since tempVar6 was passed to a method or constructor. Handle memory management manually.
                                                }

                                                train = true;
                                            }
                                        }
                                        break;
                                }
                            }
                        }
                    }
            }

                break;

            #pragma endregion

            #pragma region SlashingBurst

            case Spell::SlashingBurst:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                dir = std::any_cast<MirDirection>(data[4]);
                count = std::any_cast<int>(data[5]);

                for (int i = 0; i < count; i++)
                {
                    location = Functions::PointMove(location, dir, 1);

                    if (!ValidPoint(location))
                    {
                        continue;
                    }

                    cell = GetCell(location);

                    if (cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int o = 0; o < cell->Objects.size(); o++)
                    {
                        MapObject *target = cell->Objects[o];
                        if (target->getRace() != ObjectType::Player && target->getRace() != ObjectType::Monster)
                        {
                            continue;
                        }

                        if (!target->IsAttackTarget(player))
                        {
                            continue;
                        }
                        if (target->Attacked(player, value, DefenceType::AC, false) > 0)
                        {
                            train = true;
                        }
                        break;
                    }
                }
                break;

            #pragma endregion

            #pragma region Mirroring

            case Spell::Mirroring:
            {
                monster = std::any_cast<MonsterObject*>(data[2]);
                front = std::any_cast<Point*>(data[3]);
                bool finish = std::any_cast<bool>(data[4]);

                if (finish)
                {
                    monster->Die();
                    return;
                };

                if (ValidPoint(front))
                {
                    monster->Spawn(this, front);
                }
                else
                {
                    monster->Spawn(player->getCurrentMap(), player->getCurrentLocation());
                }
                break;

            #pragma endregion

            #pragma region Blizzard

            }
            case Spell::Blizzard:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                train = true;
                show = true;

                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid())
                        {
                            continue;
                        }

                        bool cast = true;
                        if (!cell->Objects.empty())
                        {
                            for (int o = 0; o < cell->Objects.size(); o++)
                            {
                                MapObject *target = cell->Objects[o];
                                if (target->getRace() != ObjectType::Spell || (static_cast<SpellObject*>(target))->Spell != Spell::Blizzard)
                                {
                                    continue;
                                }

                                cast = false;
                                break;
                            }
                        }

                        if (!cast)
                        {
                            continue;
                        }

                        SpellObject *ob = new SpellObject();
                        ob->Spell = Spell::Blizzard;
                        ob->Value = value;
                        ob->ExpireTime = getEnvir()->getTime() + 3000;
                        ob->TickSpeed = 440;
                        ob->Caster = player;
                        Point tempVar7(x, y);
                        ob->setCurrentLocation(&tempVar7);
                        ob->CastLocation = location;
                        ob->Show = show;
                        ob->setCurrentMap(this);
                        ob->StartTime = getEnvir()->getTime() + 800;

                        show = false;

                        AddObject(ob);
                        ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                    }
                }

                break;

            #pragma endregion

            #pragma region MeteorStrike

            case Spell::MeteorStrike:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                train = true;
                show = true;

                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid())
                        {
                            continue;
                        }

                        bool cast = true;
                        if (!cell->Objects.empty())
                        {
                            for (int o = 0; o < cell->Objects.size(); o++)
                            {
                                MapObject *target = cell->Objects[o];
                                if (target->getRace() != ObjectType::Spell || (static_cast<SpellObject*>(target))->Spell != Spell::MeteorStrike)
                                {
                                    continue;
                                }

                                cast = false;
                                break;
                            }
                        }

                        if (!cast)
                        {
                            continue;
                        }

                        SpellObject *ob = new SpellObject();
                        ob->Spell = Spell::MeteorStrike;
                        ob->Value = value;
                        ob->ExpireTime = getEnvir()->getTime() + 3000;
                        ob->TickSpeed = 440;
                        ob->Caster = player;
                        Point tempVar8(x, y);
                        ob->setCurrentLocation(&tempVar8);
                        ob->CastLocation = location;
                        ob->Show = show;
                        ob->setCurrentMap(this);
                        ob->StartTime = getEnvir()->getTime() + 800;

                        show = false;

                        AddObject(ob);
                        ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                    }
                }

                break;

            #pragma endregion

            #pragma region TrapHexagon

            case Spell::TrapHexagon:
            {
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                MonsterObject *centerTarget = nullptr;

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];

                            if (y == location->Y && x == location->X && target->getRace() == ObjectType::Monster)
                            {
                                centerTarget = static_cast<MonsterObject*>(target);
                            }

                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                {
                                    if (target == nullptr || !target->IsAttackTarget(player) || target->Node == nullptr || target->getLevel() > player->getLevel() + 2)
                                    {
                                        continue;
                                    }

                                    MonsterObject *mobTarget = static_cast<MonsterObject*>(target);

                                    if (centerTarget == nullptr)
                                    {
                                        centerTarget = mobTarget;
                                    }

                                    mobTarget->ShockTime = getEnvir()->getTime() + value;
                                    mobTarget->setTarget(nullptr);
                                    break;
                                }
                            }
                        }

                    }
                }

                if (centerTarget == nullptr)
                {
                    return;
                }

                for (unsigned char i = 0; i < 8; i += 2)
                {
                    Point *startpoint = Functions::PointMove(location, static_cast<MirDirection>(i), 2);
                    for (unsigned char j = 0; j <= 4; j += 4)
                    {
                        MirDirection spawndirection = i == 0 || i == 4 ? MirDirection::Right : MirDirection::Up;
                        Point *spawnpoint = Functions::PointMove(startpoint, spawndirection + j, 1);
                        if (spawnpoint->X <= 0 || spawnpoint->X > centerTarget->getCurrentMap()->Width)
                        {
                            continue;
                        }
                        if (spawnpoint->Y <= 0 || spawnpoint->Y > centerTarget->getCurrentMap()->Height)
                        {
                            continue;
                        }
                        SpellObject *ob = new SpellObject();
                        ob->Spell = Spell::TrapHexagon;
                        ob->ExpireTime = getEnvir()->getTime() + value;
                        ob->TickSpeed = 100;
                        ob->Caster = player;
                        ob->setCurrentLocation(spawnpoint);
                        ob->CastLocation = location;
                        ob->setCurrentMap(centerTarget->getCurrentMap());
                        ob->setTarget(centerTarget);

                        centerTarget->getCurrentMap()->AddObject(ob);
                        ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                    }
                }

                train = true;

                break;

            #pragma endregion

            #pragma region Curse

            }
            case Spell::Curse:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                value2 = std::any_cast<int>(data[4]);

                for (int y = location->Y - 3; y <= location->Y + 3; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 3; x <= location->X + 3; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:

                                    if (getEnvir()->Random->Next(10) >= 4)
                                    {
                                        continue;
                                    }

                                    //Only targets
                                    if (target->IsAttackTarget(player))
                                    {
                                        Poison *tempVar9 = new Poison(), *player;
                                        tempVar9->PType = PoisonType::Slow;
                                        tempVar9->Duration = value;
                                        tempVar9->TickSpeed = 1000;
                                        tempVar9->Value = value2;
                                        target->ApplyPoison(tempVar9, player);

                                        auto stats = new Stats();
                                        stats.[Stat::MaxDCRatePercent] = value2 * -1;
                                        stats.[Stat::MaxMCRatePercent] = value2 * -1;
                                        stats.[Stat::MaxSCRatePercent] = value2 * -1;
                                        stats.[Stat::AttackSpeedRatePercent] = target->getRace() == ObjectType::Player ? value2 * -1 : 0;

                                        target->AddBuff(BuffType::Curse, player, Settings::Second * value, stats);
                                        target->OperateTime = 0;
                                        train = true;

//C# TO C++ CONVERTER TODO TASK: A 'delete stats' statement was not added since stats was passed to a method or constructor. Handle memory management manually.
//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar9' statement was not added since tempVar9 was passed to a method or constructor. Handle memory management manually.
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region ExplosiveTrap

            case Spell::ExplosiveTrap:
            {
                value = std::any_cast<int>(data[2]);
                front = std::any_cast<Point*>(data[3]);
                int trapID = std::any_cast<int>(data[4]);

                if (ValidPoint(front))
                {
                    cell = GetCell(front);

                    bool cast = true;
                    if (!cell->Objects.empty())
                    {
                        for (int o = 0; o < cell->Objects.size(); o++)
                        {
                            MapObject *target = cell->Objects[o];
                            if (target->getRace() != ObjectType::Spell || ((static_cast<SpellObject*>(target))->Spell != Spell::FireWall && (static_cast<SpellObject*>(target))->Spell != Spell::ExplosiveTrap))
                            {
                                continue;
                            }

                            cast = false;
                            break;
                        }
                    }

                    if (cast)
                    {
                        player->LevelMagic(magic);

                        std::vector<System::Drawing::Point*> traps = std::vector<Point*>(3);
                        traps[0] = front;
                        traps[1] = Functions::Left(front, player->getDirection());
                        traps[2] = Functions::Right(front, player->getDirection());

                        for (int i = 0; i <= 2; i++)
                        {
                            SpellObject *ob = new SpellObject();
                            ob->Spell = Spell::ExplosiveTrap;
                            ob->Value = value;
                            ob->ExpireTime = getEnvir()->getTime() + (10 + value / 2) * 1000;
                            ob->TickSpeed = 500;
                            ob->Caster = player;
                            ob->setCurrentLocation(traps[i]);
                            ob->setCurrentMap(this);
                            ob->ExplosiveTrapID = trapID;
                            ob->ExplosiveTrapCount = i;

                            AddObject(ob);
                            ob->Spawned();

//C# TO C++ CONVERTER TODO TASK: A 'delete ob' statement was not added since ob was passed to a method or constructor. Handle memory management manually.
                        }
                    }
                }
                break;

            #pragma endregion

            #pragma region Plague

            }
            case Spell::Plague:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                for (int y = location->Y - 3; y <= location->Y + 3; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 3; x <= location->X + 3; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsAttackTarget(player))
                                    {
                                        int chance = getEnvir()->Random->Next(15);
                                        PoisonType poison;
                                        if ({0, 1, 3}->Contains(chance)) //3 in 15 chances it'll slow
                                        {
                                            poison = PoisonType::Slow;
                                        }
                                        else if ({3, 4}->Contains(value)) //2 in 15 chances it'll freeze
                                        {
                                            poison = PoisonType::Frozen;
                                        }
                                        else if ({5, 6, 7, 8, 9}->Contains(value)) //5 in 15 chances it'll red/green
                                        {
                                            poison = std::any_cast<PoisonType>(data[4]);
                                        }
                                        else //5 in 15 chances it'll do nothing
                                        {
                                            poison = PoisonType::None;
                                        }

                                        int tempValue = 0;

                                        if (poison == PoisonType::Green)
                                        {
                                            tempValue = value / 15 + magic->Level + 1;
                                        }
                                        else
                                        {
                                            tempValue = value + (magic->Level + 1) * 2;
                                        }

                                        if (poison != PoisonType::None)
                                        {
                                            Poison *tempVar10 = new Poison(), *player, *false, *false;
                                            tempVar10->PType = poison;
                                            tempVar10->Duration = (2 * (magic->Level + 1)) + (value / 10);
                                            tempVar10->TickSpeed = 1000;
                                            tempVar10->Value = tempValue;
                                            tempVar10->Owner = player;
                                            target->ApplyPoison(tempVar10, player, false, false);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar10' statement was not added since tempVar10 was passed to a method or constructor. Handle memory management manually.
                                        }

                                        if (target->getRace() == ObjectType::Player)
                                        {
                                            PlayerObject *tempOb = static_cast<PlayerObject*>(target);

                                            tempOb->ChangeMP(-tempValue);
                                        }

                                        train = true;
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region Trap

            case Spell::Trap:
            {
                value = std::any_cast<int>(data[2]);
                //location = (Point)data[3];
                MapObject *originalTarget = std::any_cast<MapObject*>(data[3]);
                location = originalTarget->getCurrentLocation();
                MonsterObject *selectTarget = nullptr;

                if (!ValidPoint(location))
                {
                    break;
                }

                cell = GetCell(location);

                if (!cell->getValid() || cell->Objects.empty())
                {
                    break;
                }

                for (int i = 0; i < cell->Objects.size(); i++)
                {
                    MapObject *target = cell->Objects[i];
                    if (target->getRace() == ObjectType::Monster)
                    {
                        selectTarget = static_cast<MonsterObject*>(target);

                        if (selectTarget == nullptr || !selectTarget->IsAttackTarget(player) || selectTarget->Node == nullptr || selectTarget->getLevel() >= player->getLevel() + 2)
                        {
                            continue;
                        }
                        selectTarget->ShockTime = getEnvir()->getTime() + value;
                        selectTarget->setTarget(nullptr);
                        break;
                    }
                }

                if (selectTarget == nullptr)
                {
                    return;
                }

                if (location->X <= 0 || location->X > selectTarget->getCurrentMap()->Width)
                {
                    break;
                }
                if (location->Y <= 0 || location->Y > selectTarget->getCurrentMap()->Height)
                {
                    break;
                }
                SpellObject *spellOb = new SpellObject();
                spellOb->Spell = Spell::Trap;
                spellOb->ExpireTime = getEnvir()->getTime() + value;
                spellOb->TickSpeed = 100;
                spellOb->Caster = player;
                spellOb->setCurrentLocation(location);
                spellOb->CastLocation = location;
                spellOb->setCurrentMap(selectTarget->getCurrentMap());
                spellOb->setTarget(selectTarget);

                selectTarget->getCurrentMap()->AddObject(spellOb);
                spellOb->Spawned();

                train = true;
                break;

            #pragma endregion

            #pragma region OneWithNature

//C# TO C++ CONVERTER TODO TASK: A 'delete spellOb' statement was not added since spellOb was passed to a method or constructor. Handle memory management manually.
            }
            case Spell::OneWithNature:
            {
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                bool hasVampBuff = (player->Buffs.Any([&] (std::any ex)
                {
                    return ex->Type == BuffType::VampireShot;
                }));
                bool hasPoisonBuff = (player->Buffs.Any([&] (std::any ex)
                {
                    return ex->Type == BuffType::PoisonShot;
                }));

                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (!target->IsAttackTarget(player) || target->Dead)
                                    {
                                        break;
                                    }

                                    //knockback
                                    //int distance = 1 + Math.Max(0, magic.Level - 1) + Envir.Random.Next(2);
                                    //dir = Functions.DirectionFromPoint(location, target.CurrentLocation);
                                    //if(target.Level < player.Level)
                                    //    target.Pushed(player, dir, distance);// <--crashes server somehow?

                                    if (target->Attacked(player, value, DefenceType::MAC, false) <= 0)
                                    {
                                        break;
                                    }

                                    if (hasVampBuff) //Vampire Effect
                                    {
                                        if (player->VampAmount == 0)
                                        {
                                            player->VampTime = getEnvir()->getTime() + 1000;
                                        }
                                        player->VampAmount += static_cast<unsigned short>(value * (magic->Level + 1) * 0.25F);
                                    }
                                    if (hasPoisonBuff) //Poison Effect
                                    {
                                        Poison *tempVar11 = new Poison(), *player;
                                        tempVar11->Duration = (value * 2) + (magic->Level + 1) * 7;
                                        tempVar11->Owner = player;
                                        tempVar11->PType = PoisonType::Green;
                                        tempVar11->TickSpeed = 2000;
                                        tempVar11->Value = value / 15 + magic->Level + 1 + getEnvir()->Random->Next(player->Stats[Stat::PoisonAttack]);
                                        target->ApplyPoison(tempVar11, player);
                                        target->OperateTime = 0;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar11' statement was not added since tempVar11 was passed to a method or constructor. Handle memory management manually.
                                    }
                                    train = true;
                                    break;
                            }
                        }

                    }
                }

                if (hasVampBuff)
                {
                    //Expire
                    Stats tempVar12();
                    player->AddBuff(BuffType::VampireShot, player, Settings::Second * 1, &tempVar12, visible: true);
                }
                if (hasPoisonBuff)
                {
                    //Expire
                    Stats tempVar13();
                    player->AddBuff(BuffType::PoisonShot, player, Settings::Second * 1, &tempVar13, visible: true);
                }
                break;

            #pragma endregion

            #pragma region Portal

            }
            case Spell::Portal:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);
                value2 = std::any_cast<int>(data[4]);

                spellOb = new SpellObject();
                spellOb->Spell = Spell::Portal;
                spellOb->Value = value2;
                spellOb->ExpireTime = getEnvir()->getTime() + value * 1000;
                spellOb->TickSpeed = 2000;
                spellOb->Caster = player;
                spellOb->CurrentLocation = location;
                spellOb->CurrentMap = this;

                AddObject(spellOb);
                spellOb::Spawned();
                train = true;
                break;

            #pragma endregion

            #pragma region DelayedExplosion

            case Spell::DelayedExplosion:
                value = std::any_cast<int>(data[2]);
                location = std::any_cast<Point*>(data[3]);

                for (int y = location->Y - 1; y <= location->Y + 1; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 1; x <= location->X + 1; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            switch (target->getRace())
                            {
                                case ObjectType::Monster:
                                case ObjectType::Player:
                                    //Only targets
                                    if (target->IsAttackTarget(player))
                                    {
                                        if (target->Attacked(player, value, DefenceType::MAC, false) > 0)
                                        {
                                            train = false; //wouldnt want to make the skill give twice the points
                                        }
                                    }
                                    break;
                            }
                        }

                    }

                }

                break;

            #pragma endregion

            #pragma region BattleCry

            case Spell::BattleCry:
                location = std::any_cast<Point*>(data[2]);

                for (int y = location->Y - 2; y <= location->Y + 2; y++)
                {
                    if (y < 0)
                    {
                        continue;
                    }
                    if (y >= Height)
                    {
                        break;
                    }

                    for (int x = location->X - 2; x <= location->X + 2; x++)
                    {
                        if (x < 0)
                        {
                            continue;
                        }
                        if (x >= Width)
                        {
                            break;
                        }

                        cell = GetCell(x, y);

                        if (!cell->getValid() || cell->Objects.empty())
                        {
                            continue;
                        }

                        for (int i = 0; i < cell->Objects.size(); i++)
                        {
                            MapObject *target = cell->Objects[i];
                            if (target->getRace() != ObjectType::Monster)
                            {
                                continue;
                            }

                            if (magic->Level == 0)
                            {
                                if (getEnvir()->Random->Next(60) >= 4)
                                {
                                    continue;
                                }
                            }
                            else if (magic->Level == 1)
                            {
                                if (getEnvir()->Random->Next(45) >= 3)
                                {
                                    continue;
                                }
                            }
                            else if (magic->Level == 2)
                            {
                                if (getEnvir()->Random->Next(30) >= 2)
                                {
                                    continue;
                                }
                            }
                            else if (magic->Level == 3)
                            {
                                if (getEnvir()->Random->Next(15) >= 1)
                                {
                                    continue;
                                }
                            }

                            if ((static_cast<MonsterObject*>(target))->Info->CoolEye == 100)
                            {
                                continue;
                            }
                            target->setTarget(player);
                            target->OperateTime = 0;
                            train = true;
                        }
                    }
                }
                break;

                #pragma endregion
        }

        if (train)
        {
            player->LevelMagic(magic);
        }

    }

    void Map::AddObject(MapObject *ob)
    {
        if (ob->getRace() == ObjectType::Player)
        {
            Players.push_back(static_cast<PlayerObject*>(ob));
        }

        if (ob->getRace() == ObjectType::Merchant)
        {
            NPCs.push_back(static_cast<NPCObject*>(ob));
        }
        if (ob->getRace() == ObjectType::Spell)
        {
            Spells.push_back(static_cast<SpellObject*>(ob));
        }

        GetCell(ob->getCurrentLocation())->Add(ob);
    }

    void Map::RemoveObject(MapObject *ob)
    {
        if (ob->getRace() == ObjectType::Player)
        {
            Players.Remove(static_cast<PlayerObject*>(ob));
        }
        if (ob->getRace() == ObjectType::Merchant)
        {
            NPCs.Remove(static_cast<NPCObject*>(ob));
        }
        if (ob->getRace() == ObjectType::Spell)
        {
            Spells.Remove(static_cast<SpellObject*>(ob));
        }

        GetCell(ob->getCurrentLocation())->Remove(ob);
    }

    SafeZoneInfo *Map::GetSafeZone(Point *location)
    {
        for (int i = 0; i < Info->SafeZones.size(); i++)
        {
            SafeZoneInfo *szi = Info->SafeZones[i];
            if (Functions::InRange(szi->Location, location, szi->Size))
            {
                return szi;
            }
        }
        return nullptr;
    }

    std::vector<SpellObject*> Map::GetSpellObjects(Spell spell, MapObject *caster)
    {
        std::vector<SpellObject*> spellObjects;

        for (int i = 0; i < Spells.size(); i++)
        {
            if (spell != Spells[i]->Spell)
            {
                continue;
            }
            if (caster != nullptr && Spells[i]->Caster != caster)
            {
                continue;
            }

            spellObjects.push_back(Spells[i]);
        }

        return spellObjects;
    }

    ConquestObject *Map::GetConquest(Point *location)
    {
        for (int i = 0; i < Conquest.size(); i++)
        {
            ConquestObject *swi = Conquest[i];

            if ((swi->Info->FullMap || Functions::InRange(swi->Info->Location, location, swi->Info->Size)) && swi->getWarIsOn())
            {
                return swi;
            }
        }
        return nullptr;
    }

    void Map::Broadcast(Packet *p, Point *location)
    {
        if (p == nullptr)
        {
            return;
        }

        for (int i = Players.size() - 1; i >= 0; i--)
        {
            PlayerObject *player = Players[i];

            if (Functions::InRange(location, player->getCurrentLocation(), Globals::DataRange))
            {
                player->Enqueue(p);
            }
        }
    }

    void Map::BroadcastNPC(Packet *p, Point *location)
    {
        if (p == nullptr)
        {
            return;
        }

        for (int i = Players.size() - 1; i >= 0; i--)
        {
            PlayerObject *player = Players[i];

            if (Functions::InRange(location, player->getCurrentLocation(), Globals::DataRange))
            {
                player->Enqueue(p);
            }

        }
    }

    void Map::Broadcast(Packet *p, Point *location, PlayerObject *Player)
    {
        if (p == nullptr)
        {
            return;
        }

        if (Functions::InRange(location, Player->getCurrentLocation(), Globals::DataRange))
        {
            Player->Enqueue(p);
        }
    }

    Cell *Cell::getLowWall()
    {
        Cell *tempVar = new Cell();
        tempVar->Attribute = CellAttribute::LowWall;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    Cell *Cell::getHighWall()
    {
        Cell *tempVar = new Cell();
        tempVar->Attribute = CellAttribute::HighWall;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    bool Cell::getValid() const
    {
        return Attribute == CellAttribute::Walk;
    }

    void Cell::Add(MapObject *mapObject)
    {
        if (Objects.empty())
        {
            Objects = std::vector<MapObject*>();
        }

        Objects.push_back(mapObject);
    }

    void Cell::Remove(MapObject *mapObject)
    {
        Objects.Remove(mapObject);
        if (Objects.empty())
        {
            Objects = std::vector<MapObject*>();
        }
    }

    Server::MirEnvir::Envir *MapRespawn::getEnvir()
    {
        return Envir::getMain();
    }

    MapRespawn::MapRespawn(RespawnInfo *info)
    {
        Info = info;
        Monster = getEnvir()->GetMonsterInfo(info->MonsterIndex);

        LoadRoutes();
    }

    bool MapRespawn::Spawn()
    {
        MonsterObject *ob = MonsterObject::GetMonster(Monster);
        if (ob == nullptr)
        {
            return true;
        }
        return ob->Spawn(this);
    }

    void MapRespawn::LoadRoutes()
    {
        Route = std::vector<RouteInfo*>();

        if (Info->RoutePath.empty())
        {
            return;
        }

        std::string fileName = FileSystem::combine(Settings::RoutePath, Info->RoutePath + ".txt");

        if (!FileSystem::fileExists(fileName))
        {
            return;
        }

        std::vector<std::string> lines = File::ReadAllLines(fileName).ToList();

        for (auto line : lines)
        {
            RouteInfo *info = RouteInfo::FromText(line);

            if (info == nullptr)
            {
                continue;
            }

            Route.push_back(info);
        }
    }
}
